﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mls.Interpreters;

namespace Mls
{
    public class DocumentsChain
    {
        private Document[] _documents;
        public Document[] Documents { get { return _documents; } }

        internal DocumentsChain(Document[] documents)
        {
            _documents = documents;
        }

        public override string ToString()
        {
            string text = string.Empty;

            for (int i = 0; i < _documents.Length; i++)
                text += (i > 0 ? ", " : "") + _documents[i].Name;

            return text;
        }

        internal static DocumentsChain ConsumeChain(List<Document> documents, List<string> errors)
        {
            // Start from the first document in the pool
            Document first = documents[0];
            Document last = first;
            documents.RemoveAt(0);
            List<Document> chain = new List<Document>();
            chain.Add(first);

            // Loop while pool not empty and last doc not referencing nothing or referencing first
            while (documents.Count > 0 && !string.IsNullOrEmpty(last.NextDocument) &&
                !last.NextDocument.Equals(first.Name, StringComparison.InvariantCultureIgnoreCase))
            {
                bool nextFound = false;

                // Find next document
                for (int i = 0; i < documents.Count; i++)
                {
                    if (last.NextDocument.Equals(documents[i].Name, StringComparison.InvariantCultureIgnoreCase))
                    {
                        if (!last.Name.Equals(documents[i].PreviousDocument, StringComparison.InvariantCultureIgnoreCase))
                            errors.Add(string.Format("Warning: Document {1} is back referencing {2} while the previous should be {0}",
                                last.Name, documents[i].Name, documents[i].PreviousDocument));

                        nextFound = true;
                        last = documents[i];
                        chain.Add(last);
                        documents.RemoveAt(i--);
                        break;
                    }
                }

                // If next not found, add error message and end loop
                if (!nextFound)
                {
                    errors.Add(string.Format("Error: Document {0} is referencing a not found document {1}", last.Name, last.NextDocument));
                    break;
                }
            }

            // If last document is referencing nothing, then do previous documents search
            if (string.IsNullOrEmpty(last.NextDocument))
            {
                last = first;

                // Loop while pool not empty and last doc not referencing nothing or referencing first
                while (documents.Count > 0 && !string.IsNullOrEmpty(last.PreviousDocument) &&
                    !last.PreviousDocument.Equals(first.Name, StringComparison.InvariantCultureIgnoreCase))
                {
                    bool previousFound = false;

                    // Find next document
                    for (int i = 0; i < documents.Count; i++)
                    {
                        if (last.PreviousDocument.Equals(documents[i].Name, StringComparison.InvariantCultureIgnoreCase))
                        {
                            if (!last.Name.Equals(documents[i].NextDocument, StringComparison.InvariantCultureIgnoreCase))
                                errors.Add(string.Format("Warning: Document {1} is referencing {2} while the previous should be {0}",
                                    last.Name, documents[i].Name, documents[i].NextDocument));

                            previousFound = true;
                            last = documents[i];
                            chain.Insert(0, last);
                            documents.RemoveAt(i--);
                            break;
                        }
                    }

                    // If next not found, add error message and end loop
                    if (!previousFound)
                    {
                        errors.Add(string.Format("Error: Document {0} is back-referencing a not found document {1}", last.Name, last.PreviousDocument));
                        break;
                    }
                }
            }

            // Validate if any of the left documents referring to chain items

            foreach (Document leftDocument in documents)
                foreach (Document chainItem in chain)
                    if (leftDocument.PreviousDocument.Equals(chainItem.Name, StringComparison.InvariantCultureIgnoreCase) ||
                        leftDocument.NextDocument.Equals(chainItem.Name, StringComparison.InvariantCultureIgnoreCase))
                        errors.Add(string.Format("Warning: Document {0} is referencing document {1} while it is already in a chain",
                            leftDocument.Name, chainItem.Name));

            return new DocumentsChain(chain.ToArray());
        }

        /*public void Parse(IMlsInterpreter interpreter)
        {
            foreach (Document document in _documents)
                document.ParseDocument(interpreter);
        }*/
    }
}
